package de.in.tum;

import java.util.ArrayList;

import android.app.Activity;
import android.graphics.Matrix;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.Toast;

public class Androidhw2Activity extends Activity implements OnTouchListener {
	private ArrayList<AdvancedGestureListener> listeners;
	
    // Matrix instances to move and zoom image
    private Matrix matrix = new Matrix();
    private Matrix eventMatrix = new Matrix();

    // possible touch states
    private final static int NONE = 0;
    private final static int DRAG = 1;
    private final static int ZOOM = 2;
    private int touchState = NONE;
    
    private final static float MIN_DIST = 50;
    private final static float DEVIATION = 2; // minimum distance for a valid move
    private float eventDistance = 0;
    private float centerX = 0, centerY = 0;

    private String toast_message;
    
    private long time;
	
    public Androidhw2Activity() {
    	listeners = new ArrayList<AdvancedGestureListener>();
    	toast_message = "";
    }
    
    // Called to register a gesture listener
    public final void register(AdvancedGestureListener listener) {
    	if (!listeners.contains(listener))
    		listeners.add(listener);
    }
	
    /* Called to unregister a gesture listener.
     * Returns true only if listener was registered before.
     */
    public boolean unregister(AdvancedGestureListener listener) {
    	if (listeners.contains(listener)) {
    		listeners.remove(listener);
    		return true;
    	}
    	return false;
    }
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ImageView view = (ImageView) findViewById(R.id.imageView);
        view.setOnTouchListener(this);
    }

	@Override
	public boolean onTouch(View v, MotionEvent event) {
        ImageView view = (ImageView) v;

        switch (event.getAction() & MotionEvent.ACTION_MASK) {
	        case MotionEvent.ACTION_DOWN:
	        	long time2 = System.currentTimeMillis();
	        	// decide whether double tap or at least one tap;
	        	Gesture gesture = time2 - time < 600 ? Gesture.DOUBLE_TAB : Gesture.TAP;
	        	// notify listeners
	        	notify(gesture, v, event);
	        	time = time2;
	        	Log.d(event.toString(), gesture.toString());
	        	// verify whether this toast is similar to the previous one
	        	if (gesture.toString() != toast_message)
	        	  Toast.makeText(this, toast_message = gesture.toString(), Toast.LENGTH_SHORT).show();

	        	//primary touch event starts: remember touch down location
	            touchState = DRAG;
	            centerX = event.getX(0);
	            centerY = event.getY(0);
	            eventMatrix.set(matrix);
	            break;
	
	        case MotionEvent.ACTION_POINTER_DOWN:
	            //secondary touch event starts: remember distance and center
	            eventDistance = calcDistance(event);
	            calcMidpoint(centerX, centerY, event);
	            if (eventDistance > MIN_DIST) {
	                eventMatrix.set(matrix);
	
	                touchState = ZOOM;
	            }
	            break;
	
	        case MotionEvent.ACTION_MOVE:
	            if (touchState == DRAG) {
	                float dx = event.getX(0) - centerX; // horizontal distance
	                float dy = event.getY(0) - centerY; // vertical distance
	                float dist = FloatMath.sqrt(dx * dx + dy * dy); // distance
	                float adx = Math.abs(dx); // positive horizontal distance
	                float ady = Math.abs(dy); // positive vertical distance
	                
	                // valid move if dist >= 5
	                if (dist < 5)
	                	break;

	                // transfers the origin of the current move further to the actual point
	                // in order to notice little opposite changes
            		if (dist > 20) {
            			centerX += dx * 0.7;
            			centerY += dy * 0.7;
            		}

            		// calculate direction
	                gesture = null;
	                if (adx > ady) { // big move left or right
	                	if (adx / ady > DEVIATION) { // small vertical deviation
	                		gesture = dx > 0 ? Gesture.MOVE_RIGHT : Gesture.MOVE_LEFT;
	                		dy = 0; // just horizontal
	                	}
	                }
	                else
	                	if (ady / adx > DEVIATION) { // small horizontal deviation
	                		gesture = dy > 0 ? Gesture.MOVE_DOWN : Gesture.MOVE_UP;
	                		dx = 0; // just vertical
	                	}
	                if (gesture == null) // diagonal
	                	if (dx > 0)
	                		gesture = dy > 0 ? Gesture.MOVE_RIGHT_DOWN : Gesture.MOVE_RIGHT_UP;
	                	else
	                		gesture = dy > 0 ? Gesture.MOVE_LEFT_DOWN : Gesture.MOVE_LEFT_UP;

	                notify(gesture, v, event);
		        	Log.d(event.toString(), gesture.toString());
		        	// verify whether this toast is similar to the previous one
		        	if (gesture.toString() != toast_message)
			        	  Toast.makeText(this, toast_message = gesture.toString(), Toast.LENGTH_SHORT).show();
		        	// single finger drag, translate accordingly
	                matrix.set(eventMatrix);
	                matrix.setTranslate(dx, dy);
	
	            } else if (touchState == ZOOM) {
	                //multi-finger zoom, scale accordingly around center
	                float dist = calcDistance(event);
	
	                if (dist > MIN_DIST) {
	                    matrix.set(eventMatrix);
	                    float scale = dist / eventDistance;
	
	                    matrix.postScale(scale, scale, centerX, centerY);
	                    gesture = Gesture.ZOOM;
	    	        	Log.d(event.toString(), gesture.toString());
	    	        	// verify whether this toast is similar to the previous one
	    	        	if (gesture.toString() != toast_message)
	    	        	  Toast.makeText(this, toast_message = gesture.toString(), Toast.LENGTH_SHORT).show();
	                }
	            }
	
	            // Perform the transformation
	            view.setImageMatrix(matrix);
	            break;
	
	        case MotionEvent.ACTION_UP:
	        case MotionEvent.ACTION_POINTER_UP:
	            touchState = NONE;
	            break;
        }       
        return true; 
    }
	
	private final void notify(Gesture gesture, View v, MotionEvent event) {
		for (AdvancedGestureListener listener : listeners)
			listener.gesture(gesture, v, event);
	}
	
    private final float calcDistance(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return FloatMath.sqrt(x * x + y * y);
    }

    private final void calcMidpoint(float centerX, float centerY, MotionEvent event) {
        centerX = (event.getX(0) + event.getX(1))/2;
        centerY = (event.getY(0) + event.getY(1))/2;
    }
}